﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.Generic;
using System.Diagnostics;
using System.Windows.Threading;
using System.Collections.ObjectModel;
using System.Linq;

namespace BattleforgeAssault
{
    /// <summary>
    /// A player in the game.
    /// </summary>
    /// <remarks>Base class for AI opponents</remarks>
    public class Player : GameObject
    {
        #region Properties

        #region Modified Stats

        private int _resources = 0;
        public int Resources { get { return Game.GetModified(this, "Resources", _resources); } set { _resources = value; RaisePropertyChanged("Resources"); } }

        #endregion Modified Stats

        #region Locations

        private Deck _deck = new Deck();
        public Deck Deck { get { return _deck; } set { _deck = value; RaisePropertyChanged("Deck"); } }

        private Hand _hand = new Hand();
        public Hand Hand { get { return _hand; } set { _hand = value; RaisePropertyChanged("Hand"); } }

        private Discard _discard = new Discard();
        public Discard Discard { get { return _discard; } set { _discard = value; RaisePropertyChanged("Discard"); } }

        private KingdomZone _kingdom = new KingdomZone();
        public KingdomZone Kingdom { get { return _kingdom; } set { _kingdom = value; RaisePropertyChanged("Kingdom"); } }

        private BattlefieldZone _battlefield = new BattlefieldZone();
        public BattlefieldZone Battlefield { get { return _battlefield; } set { _battlefield = value; RaisePropertyChanged("Battlefield"); } }

        private QuestZone _quest = new QuestZone();
        public QuestZone Quest { get { return _quest; } set { _quest = value; RaisePropertyChanged("Quest"); } }

        #endregion Locations

        private Types.Races _race = Types.Races.Neutral;
        public Types.Races Race { get { return _race; } set { _race = value; RaisePropertyChanged("Race"); } }

        private Game _game;
        public Game Game { get { return _game; } }

        private string _name = "Player";
        public string Name { get { return _name; } set { _name = value; RaisePropertyChanged("Name"); } }

        public IEnumerable<Player> Opponents { get { return Game.Players.Where(player => player != this); } }

        public IEnumerable<Card> Cards { get { return Game.Cards.Where(card => card.Controller == this); } }

        private int _numattacks = 1; //the number of zones you can attack during your battlefield phase, almost always 1
        public int NumAttacks { get { return _numattacks; } set { _numattacks = value; RaisePropertyChanged("NumAttacks"); } }
        
        #endregion Properties

        public Player(Game game)
        {
            Messager.SendMessage("Player Created: {0}", Name);
            _game = game;
            Hand.Owner = this;
            Deck.Owner = this;
            Discard.Owner = this;
            Kingdom.Owner = this;
            Battlefield.Owner = this;
            Quest.Owner = this;
        }

        /// <summary>
        /// Query the user on an action to take
        /// </summary>
        /// <param name="message">The message to prompt the user.</param>
        /// <returns>
        /// The selected action or null if the user passed.
        /// </returns>
        public virtual Action SelectAction(string message)
        {
            var targets = SelectTargets(message, 0, 1, (t) =>
            {
                if (t is Card)
                {
                    Card card = t as Card;
                    if (card.Controller != this) { return false; }
                    if (card.Action.Applicable(card) && (card.Location.IsInPlay || (card is Tactic && card.Location is Hand))) { return true; }
                }
                return false;
            });
            if (targets != null && targets.Count > 0 && targets[0] is Card) { return ((Card)targets[0]).Action; }
            return null;
        }
        public Action SelectAction() { return SelectAction("Choose Action or Pass"); }

        /// <summary>
        /// Allow the user to select a card.
        /// </summary>
        /// <param name="message">The message to prompt to the user.</param>
        /// <returns>
        /// The card that the user selected or null if passed.
        /// </returns>
        public virtual Card SelectCard(string message)
        {
            var targets = SelectTargets(message, 0, 1, (t) =>
            {
                return (t is Card && ((Card)t).Cost <= Resources);
            });
            if (targets != null && targets.Count > 0 && targets[0] is Card) { return ((Card)targets[0]); }
            return null;
        }
        public Card SelectCard() { return SelectCard("Choose a Card or Pass"); }

        public virtual List<GameObject> SelectTargets(string message, int min, int max, Func<GameObject, bool> targetIsValid)
        {
            if (Game.State != Types.States.Playing) { Messager.SendError("Already selecting targets"); return null; }
            Messager.SendMessage("{0} is selecting targets", Name);
            Game.State = Types.States.Targeting;
            Game.Controller.SelectTargets(message, min, max, targetIsValid);
            Game.WaitForInput();
            Game.State = Types.States.Playing;

            string targetNames = "";
            if (Game.Controller.CurrentTargets.Count < 1) targetNames = "Nothing";
            else if (Game.Controller.CurrentTargets.Count > 1)
            {
                foreach (var target in Game.Controller.CurrentTargets) { targetNames += target + " "; }
            }
            else
            {
                targetNames = Game.Controller.CurrentTargets[0].ToString();
            }
            Messager.SendMessage("{0} has selected {1}", Name, targetNames);

            return Game.Controller.CurrentTargets;
        }

        public virtual int AnswerQuery(Query query)
        {
            Messager.SendMessage("{0} is being queried", Name);
            Game.State = Types.States.Querying;
            Game.Controller.QueryPlayer(query);
            Game.WaitForInput();
            Game.State = Types.States.Playing;
            Messager.SendMessage("{0} chose option {1}", Name, Game.Controller.CurrentQuery.Answer);
            return Game.Controller.CurrentQuery.Answer;
        }

        public void DiscardCard(Card card)
        {
            Messager.SendMessage("Discarding {0} from {1}", card, card.Location);
            card.Location = Discard;
            card.LeavePlay();
        }

        /// <summary>
        /// Move a card from one location to another and call its EnterPlay function
        /// </summary>
        /// <param name="card">The card to move</param>
        /// <param name="location">The location to move to</param>
        public void PlayCardTo(Card card, Location location)
        {
            // add to zone
            Messager.SendMessage("Playing {0} from {1} to {2}", card, card.Location, location);

            if (Resources < card.Cost) { Messager.SendError("{0} does not have {1} resource(s)", this, card.Cost); }

            // TODO: currently allows play despite error (for debug) - fix this
            this.Resources -= card.Cost;
            card.Location = location;
            card.EnterPlay();
        }

        /// <summary>
        /// Move a card from one location to another but do not call its EnterPlay function
        /// </summary>
        /// <param name="card">The card to move</param>
        /// <param name="location">The location to move to</param>
        public void MoveCardTo(Card card, Location location)
        {
            // add to zone
            Messager.SendMessage("Moving {0} from {1} to {2}", card, card.Location, location);
            card.Location = location;
        }

        /// <summary>
        /// Move cards from the player's Deck to their Hand
        /// </summary>
        /// <param name="number"></param>
        public void DrawCards(int number)
        {
            for (int i = 0; i < number; ++i)
            {
                int lastIndex = Deck.Cards.Count() - 1;
                if (lastIndex >= 0)
                {
                    Card card = Deck.Cards.Last();
                    card.Location = Hand;
                    Messager.SendMessage("{0} drew a {1}", this, card);
                }
            }
        }

        /// <summary>
        /// Uncorrupt (untap) a number of units
        /// </summary>
        /// <param name="number">The number of units the player can uncorrupt</param>
        public void UncorruptUnit(int number)
        {
            //var targets = from GameObject t in
            //                  Battlefield.Select(b => (GameObject)b)
            //                  .Concat(Kingdom.Select(k => (GameObject)k))
            //                  .Concat(Quest.Select(q => (GameObject)q))
            //              where t is Card
            //              where ((Card)t).IsCorrupt
            //              select t;
            //Game.ValidTargets.Clear();
            //foreach (var target in targets)
            //{
            //    Game.ValidTargets.Add(target);
            //}

            //Game.State = Types.States.Targeting;

            //Game.TargetsSelected += (s, e) =>
            //{
            //    // TODO: correct this error
            //    if (Selected.Count > number) { Messager.SendError("Too many units were selected"); return; }

            //    foreach (var obj in Game.SelectedTargets)
            //    {
            //        if (Game.ValidTargets.Contains(obj))
            //        {
            //            if (obj is Card)
            //            {
            //                ((Card)obj).Corrupt(false);
            //            }
            //        }
            //        else
            //        {
            //            Messager.SendError("Not a valid target");
            //        }
            //    }
            //};

            //// choose target from all corrupted units
            //var targets = from GameObject t in 
            //    Battlefield.Select(b=>(GameObject)b)
            //    .Concat(Kingdom.Select(k=>(GameObject)k))
            //    .Concat(Quest.Select(q=>(GameObject)q))
            //    where t is Card
            //    where ((Card)t).IsCorrupt
            //    select t;

            //Card selectedTarget = _game.SelectTarget(targets) as Card;
            //if (selectedTarget != null) { selectedTarget.Corrupt(false); }
        }

        #region Helper Methods

        public override string ToString()
        {
            return Name;
        }

        #endregion Helper Methods

        public void Reset()
        {
            // TODO: Shuffle ALL cards into the deck
            int cardsToMove = Hand.Cards.Count() - 1;
            for (int i = 0; i < cardsToMove; ++i)
            {
                MoveCardTo(Hand.Cards.First(), Deck);
            }
        }
    } // class
} // namespace